package de.bht.fb6.cg1.exercise3.implement;

import java.lang.reflect.Array;

import de.bht.fb6.cg1.exercise3.Ring;

/**
 * ring implementation of the columnvector
 *
 * @param <T> object to calculate with
 */
public class ColumnVectorRing<T extends Number> implements Ring<ColumnVectorImpl<T>> {

	/**
	 * ring class - provieds default calculation methods
	 */
	private Class<? extends Ring<T>> atomRingClass;

	/**
	 * internal value vector.
	 */
	private ColumnVectorImpl<T> vector;

	/**
	 * create a new {@link ColumnVectorRing} to a specefic Ring
	 * @param atomRingClass a typed Ring
	 */
	public ColumnVectorRing(final Class<? extends Ring<T>> atomRingClass) {
		this.atomRingClass = atomRingClass;
		vector = zero().get();
	}

	/**
	 * Create a new {@link ColumnVectorRing} to a given {@link MatrixImpl}
	 * @param v matrix impl to initialise
	 */
	public ColumnVectorRing(final MatrixImpl<T> v) {
		set(new ColumnVectorImpl<T>(v));
	}

	/**
	 * add a value to the ring
	 * @param n value to add
	 * @return calculation result
	 */
	@Override
	public Ring<ColumnVectorImpl<T>> add(final Ring<ColumnVectorImpl<T>> n) {
		return new ColumnVectorRing<T>(vector.add(n.get()));
	}

	/**
	 * get the internal vector
	 * @return a {@link ColumnVectorImpl}
	 */
	@Override
	public ColumnVectorImpl<T> get() {
		return vector;
	}

	/**
	 * create the neutral element.
	 * @return neutral element (identiy matrix)
	 */
	@SuppressWarnings("unchecked")
	@Override
	public Ring<ColumnVectorImpl<T>> one() {
		final Ring<T>[][] tOne = (Ring<T>[][]) Array.newInstance(atomRingClass, 1, 1);
		tOne[0][0] = zero().get().createId(1, 1).getRing(0, 0);
		return new ColumnVectorRing<T>(new ColumnVectorImpl<T>(tOne));
	}

	/**
	 * set the internal vector to a new {@link ColumnVectorImpl}
	 * @param n the new vector
	 */
	@Override
	public void set(final ColumnVectorImpl<T> n) {
		vector = n;
		atomRingClass = n.getRingClass();
	}

	/**
	 * subtract n from the internal value.
	 * @param n value to subtract
	 * @return result of subtraction
	 */
	@Override
	public Ring<ColumnVectorImpl<T>> sub(final Ring<ColumnVectorImpl<T>> n) {
		return new ColumnVectorRing<T>(vector.sub(n.get()));
	}

	/**
	 * multiplies the Ring n times
	 * @param n multiplier
	 * @return result of multipl
	 */
	@Override
	public Ring<ColumnVectorImpl<T>> times(final Ring<ColumnVectorImpl<T>> n) {
		if (vector.getColumns() != n.get().getRows()) {
			return n.times(this);
		}
		return new ColumnVectorRing<T>(vector.mult(n.get()));
	}

	/**
	 * get the 0 element.
	 * @result 0 elemnt vector.
	 */
	@SuppressWarnings("unchecked")
	@Override
	public Ring<ColumnVectorImpl<T>> zero() {
		Ring<T> scalarRingElement;
		try {
			scalarRingElement = atomRingClass.newInstance();
		} catch (final Exception e) {
			throw new IllegalStateException("FIXME");
		}
		scalarRingElement = scalarRingElement.zero();
		final Ring<T>[][] start_val = (Ring<T>[][]) Array.newInstance(atomRingClass, 1, 1);
		start_val[0][0] = scalarRingElement.zero();
		return new ColumnVectorRing<T>(new ColumnVectorImpl<T>(start_val));
	}

	/**
	 * creates a numeric hash
	 * @return numeric hashcode
	 */
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result
				+ ((atomRingClass == null) ? 0 : atomRingClass.hashCode());
		result = prime * result + ((vector == null) ? 0 : vector.hashCode());
		return result;
	}

	/**
	 * compares to another obj
	 * @param obj the other obj
	 * @return true if objects are equal
	 */
	@SuppressWarnings("unchecked")
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		ColumnVectorRing<T> other = (ColumnVectorRing<T>) obj;
		if (atomRingClass == null) {
			if (other.atomRingClass != null)
				return false;
		} else if (!atomRingClass.equals(other.atomRingClass))
			return false;
		if (vector == null) {
			if (other.vector != null)
				return false;
		} else if (!vector.equals(other.vector))
			return false;
		return true;
	}

}
